﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.IO;
using System.Web;
using System.Net;
using EventbriteService.Model;
using EventbriteService.Util;
using EventbriteService.Adapter;
using EventbriteService.Factory;

namespace EventbriteService
{
   
    public  class Client : IEventbriteClient , IEventBriteConfiguration 
    {
        IEventbriteFactory<IDataAdapter> _dataAdapterFactory;
        IUrlBuilder _urlBuilder;
        IClientSource _source;

        public Client()
        {
            _urlBuilder = new EventbriteFactory<IUrlBuilder>().GetInstance();
            _dataAdapterFactory = new EventbriteFactory<IDataAdapter>();
            _source = new EventbriteFactory<IClientSource>().GetInstance();
            MessageFormat = Util.MessageFormat.JSON;
        }
        
        public string UserKey { get; set; }
        public string AppKey { get; set; }
        public string RequestURL { get; set; }
        public string MessageFormat { get;private set; }
        public TextWriter LogResult { get; set; }

       
        public string GetResponse(Uri requestUrl)
        {
            string doc = _source.Read(requestUrl);
            if (LogResult != null)
                LogResult.Write(doc);
            return doc; 
        }

        public IEnumerable<Format> GetFormats()
        {
            var requestString = CreateRequestForMethod(APIMethods.GetFormats)
                                        .Build();
            return Map<Format>(GetResponse(requestString));
        }
       
        public IEnumerable<Category> GetCategories()
        {
            var requestString = CreateRequestForMethod(APIMethods.GetCategories)
                                         .Build();
            return Map<Category>(GetResponse(requestString));
        }
       
        public IEnumerable<Order> GetUserOrders(long? id)
        {
            var requestString = CreateRequestForMethod(String.Format(APIMethods.GetUserOrder,id == null ? "me" : id.Value.ToString()))
                                         .Build();
            return Map<Order>(GetResponse(requestString));
        }
       
        public IEnumerable<Order> GetEventOrders(long eventId)
        {
            var requestString = CreateRequestForMethod(String.Format(APIMethods.EventOrders, eventId))
                                           .Build();
            return Map<Order>(GetResponse(requestString));
        }
        
        public Order GetOrder(long id)
        {
            var requestString = CreateRequestForMethod(String.Format(APIMethods.GetOrder, id))
                                         .Build();
           
            return MapInstance<Order>(GetResponse(requestString));
        }
       
               
        public IEnumerable<Attendee> EventAttendees(long eventId)
        {

            var requestString = CreateRequestForMethod(String.Format(APIMethods.EventListAttendees,eventId))
                                          .Build();
           
            return Map<Attendee>(GetResponse(requestString));
        }

        public Event GetEvent(long eventId)
        {
            
            var requestString = CreateRequestForMethod(String.Format(APIMethods.EventGet, eventId))
                                        .Expand()
                                        .Build();

            return MapInstance<Event>(GetResponse(requestString));
        }
        
        public Venue GetVenue(int venueId)
        {
            var requestString = CreateRequestForMethod(String.Format(APIMethods.VenueGet, venueId))
                                         .Build();

            return MapInstance<Venue>(GetResponse(requestString));
        }

        public Organizer GetOrganizer(long organizerId)
        {
            var requestString = CreateRequestForMethod(string.Format(APIMethods.OrganizerGet, organizerId))
                                         .Build();

            return MapInstance<Organizer>(GetResponse(requestString));
        }

        public IEnumerable<Event> GetUserEvents()
        {
            var requestString = CreateRequestForMethod(String.Format(APIMethods.UserListEvents, "me"))
                                          .Build();

            return Map<Event>(GetResponse(requestString));
        }

        public User GetUser()
        {
            var requestString = CreateRequestForMethod(String.Format(APIMethods.UserGet, "me")).Build();

            return MapInstance<User>(GetResponse(requestString));
        }

        public IEnumerable<Venue> GetUserVenues()
        {
            var requestString = CreateRequestForMethod(String.Format(APIMethods.UserListVenues,"me")).Build();

            return Map<Venue>(GetResponse(requestString));
        }

        public IEnumerable<Organizer> GetUserOrganizers()
        {
            var requestString = CreateRequestForMethod(String.Format(APIMethods.UserListOrganizers, "me")).Build();

            return Map<Organizer>(GetResponse(requestString));
        }

        
       
       protected IUrlBuilder CreateRequestForMethod(string command)
       {
            return _urlBuilder.AddConfiguration(this)
                               .AddRequestBaseUrl()
                               .AddRequestMethod(command);
      }

       public IDataAdapter CreateAdapter(string doc)
       {
           var data = _dataAdapterFactory.GetInstance(MessageFormat, doc);

           if (data.IsErrorMessage())
           {
               var errorMessage = data.Root.Elements().First(e => e.Name == "error_message").Value;
               var errorType = data.Root.Elements().First(e => e.Name == "error_type").Value;
               throw new EventbriteException(errorMessage, errorType);
           }

           return data;
       }

       private IEnumerable<T> Map<T>(string doc)
       {
           IDataAdapter adapter = CreateAdapter(doc);
           Mapper<T> mapper = new Mapper<T>(adapter);
           return mapper.Build();
       }

       private T MapInstance<T>(string doc)
       {
           IDataAdapter adapter = CreateAdapter(doc);
           Mapper<T> mapper = new Mapper<T>(adapter);
           return mapper.BuildInstance();
       }

     
    }
}
